package eiisan.config.security.filter;

import com.auth0.jwt.exceptions.TokenExpiredException;
import com.fasterxml.jackson.databind.ObjectMapper;
import eiisan.config.security.util.JwtTokenUtil;
import eiisan.config.security.util.Matcher;
import eiisan.util.http.ServletUtil;
import eiisan.util.model.response.Result;
import eiisan.util.model.response.ResultCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@Slf4j
@Component
public class JwtAuthorizationTokenFilter extends OncePerRequestFilter {
    @Autowired
    private ObjectMapper objectMapper;

    //    @Autowired
//    @Qualifier("eiisanDTS")
//    private UserDetailsService userDetailsService;
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
        if (Matcher.needVerify(request.getServletPath())) {
            String authToken = JwtTokenUtil.getRealToken(request);
            if (!StringUtils.isEmpty(authToken)) {
                // 将携带的token还原成用户信息
                UserDetails user = null;
                try {
                    user = JwtTokenUtil.verify(authToken);
                } catch (TokenExpiredException e) {
                    //1.如果token超时失效,这里不删除token,而是告诉客户端token失效,让客户端重新登陆.
                    ServletUtil.writeJson(response, Result.expired());
                   /* Result result = Result.expired();
                    //HttpStatus 状态码
                    response.setStatus(ResultCode.of(result.getCode()).getHttpStatus().value());
                    response.setCharacterEncoding("UTF-8");
                    response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
                    PrintWriter out = null;
                    try {
                        out = response.getWriter();
                        out.append(objectMapper.writeValueAsString(result));
                        out.flush();
                    } finally {
                        if (out != null) {
                            out.close();
                        }
                    }*/
                   //2.或者直接将user置空，不做其他处理
                    // user = null;
                }
                if (user != null && SecurityContextHolder.getContext().getAuthentication() == null) {
                    // It is not compelling necessary to load the use details from the database. You could also store the information
                    // in the token and read it from it. It's up to you ;
                    // 这里直接使用了token里的userDetails，就不从数据库重新获取了
                    // UserDetails userDetails = userDetailsService.loadUserByUsername(user.getUsername());
                    // UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                    UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(user,
                            null, user.getAuthorities());
                    // For simple validation it is completely sufficient to just check the token integrity. You don't have to call
                    // the database compellingly. Again it's up to you ;
                    authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                }
            }
        }
        chain.doFilter(request, response);
    }
}
